import {ProgramConfiguration, ConstantBinder} from "../src/data/ProgramConfiguration";
import {Program} from "../src/render/Program";
import {VertexBuffer} from "../src/gl/VertexBuffer";
import {Util} from "../src/util/Util";
import {Transform} from "../src/geo/Transform";
import {LngLat} from "../src/geo/LngLat";
import {TileCoord} from "../src/source/TileCoord";
import shaders from "../src/shaders";
import {StructArray, ViewType, createStructArrayType} from "../src/util/StructArray";
import {ImageManager} from "../src/render/ImageManager";
import {ImagePosition} from "../src/render/ImageAtlas";
import {StyleImage} from "../src/style/StyleImage";
import {loadSprite} from "../src/style/SpriteLoad";

window.onload = () => {
  const context: WebGLRenderingContext | null = Util.getWebGLContext(<HTMLCanvasElement>document.getElementById("canvas"), 800, 600);
  let gl: WebGLRenderingContext;
  if (context != null) {
    gl = <WebGLRenderingContext>context;
  } else {
    throw new Error("WebGL not available");
  }

  const programConfiguration = new ProgramConfiguration();
  programConfiguration.interface = {
    layoutAttributes: [
        {name: "a_pos", components: 2, type: ViewType.Int16}
    ]
  };
  programConfiguration.binders.opacity = new ConstantBinder("opacity", "number", "fill-opacity", false);

  const program = new Program(gl, shaders.fillPattern, programConfiguration, false);

  gl.useProgram(program.program);
  programConfiguration.binders.opacity.setUniforms(gl, program, 1.0);

  const PosArray = createStructArrayType({
    members: programConfiguration.interface.layoutAttributes
  });
  const structArray = new PosArray();
  const extent = 8192;
  structArray.emplaceBack(0, 0);
  structArray.emplaceBack(extent, 0);
  structArray.emplaceBack(extent, extent);
  structArray.emplaceBack(extent, extent);
  structArray.emplaceBack(0, 0);
  structArray.emplaceBack(0, extent);

  const vb = new VertexBuffer(gl, structArray);
  vb.enableAttributes(gl, program);
  vb.setVertexAttribPointers(gl, program);

  // Create a texture.
  // var texture = gl.createTexture();
  // gl.bindTexture(gl.TEXTURE_2D, texture);
  // // Fill the texture with a 1x1 blue pixel.
  // gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
  //               new Uint8Array([0, 0, 255, 255]));
  // Asynchronously load an image
  // var image = new Image();
  // image.src = "./flag.png";
  // image.addEventListener('load', function() {
  //   // Now that the image has loaded make copy it to the texture.
  //   gl.bindTexture(gl.TEXTURE_2D, texture);
  //   gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
  //   gl.generateMipmap(gl.TEXTURE_2D);

  //   drawScene();
  // });
  const imageManager = new ImageManager();
  loadSprite("./streets-v9", (err, images) => {
    if (err) {
      console.warn(err);
      return;
    }
    imageManager.images = <{ [key: string]: StyleImage }>images;
    const positionA = imageManager.getPattern("golf-11");
    const positionB = imageManager.getPattern("pedestrian-polygon");
    if (positionA && positionB) {
      drawScene(positionA, positionB);
    }
  });

  function drawScene(posA: ImagePosition, posB: ImagePosition) {
    const transform = new Transform();
    const tileCoor = new TileCoord(0,0,0);
    transform.resize(800, 600);
    transform.center = new LngLat(0, 0);
    const posMatrix = transform.calculatePosMatrix(tileCoor);

    gl.uniformMatrix4fv(<WebGLUniformLocation>program.uniforms.u_matrix, false,
      posMatrix);

    gl.uniform1i(program.uniforms.u_image, 0);
    gl.uniform2fv(<WebGLUniformLocation>program.uniforms.u_pattern_tl_a, posA.tl);
    gl.uniform2fv(<WebGLUniformLocation>program.uniforms.u_pattern_br_a, posA.br);
    gl.uniform2fv(<WebGLUniformLocation>program.uniforms.u_pattern_tl_b, posB.tl);
    gl.uniform2fv(<WebGLUniformLocation>program.uniforms.u_pattern_br_b, posB.br);
    const {width, height} = imageManager.getPixelSize();
    gl.uniform2fv(<WebGLUniformLocation>program.uniforms.u_texsize, [width, height]);
    gl.uniform1f(<WebGLUniformLocation>program.uniforms.u_mix, 0.5);
    gl.uniform2fv(<WebGLUniformLocation>program.uniforms.u_pattern_size_a, posA.displaySize);
    gl.uniform2fv(<WebGLUniformLocation>program.uniforms.u_pattern_size_b, posB.displaySize);
    gl.uniform1f(<WebGLUniformLocation>program.uniforms.u_scale_a, 1.0);
    gl.uniform1f(<WebGLUniformLocation>program.uniforms.u_scale_b, 1.0);

    const tileSize = 512;
    gl.uniform1f(program.uniforms.u_tile_units_to_pixels, 1 / Util.pixelsToTileUnits(
      {coord: tileCoor, tileSize: tileSize}, 1, 0));

    const numTiles = Math.pow(2, tileCoor.z);
    const tileSizeAtNearestZoom = tileSize * Math.pow(2, 0) / numTiles;

    const pixelX = tileSizeAtNearestZoom * (tileCoor.x + tileCoor.w * numTiles);
    const pixelY = tileSizeAtNearestZoom * tileCoor.y;

    // split the pixel coord into two pairs of 16 bit numbers. The glsl spec only guarantees 16 bits of precision.
    // pixel_coord_upper = floor(pixel_coord / 2^16)
    gl.uniform2f(program.uniforms.u_pixel_coord_upper, pixelX >> 16, pixelY >> 16);
    // pixel_coord_lower = mod(pixel_coord, 2^16)
    gl.uniform2f(program.uniforms.u_pixel_coord_lower, pixelX & 0xFFFF, pixelY & 0xFFFF);

    gl.activeTexture(gl.TEXTURE0);
    imageManager.bind(gl);

    gl.drawArrays(gl.TRIANGLES, 0, 3);
  }
};